// ============================================================================
//
// Copyright (C) 2006-2007 Dengues
//
// Google Group: http://groups.google.com/group/dengues
// QQ Group: 24885404
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// ============================================================================
package org.dengues.commons.ui.swt;

import java.lang.reflect.Method;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

/**
 * Qiang.Zhang.Adolf@gmail.com class global comment. Detailled comment <br/>
 * 
 * $Id: Dengues.epf 2008-1-6 qiang.zhang $
 * 
 */
public class SwtUtils {

    public static final Font BOLD = JFaceResources.getFontRegistry().getBold(JFaceResources.DEFAULT_FONT);

    public static final Color COLOR_HYPERLINK_WIDGET = new Color(Display.getDefault(), 12, 81, 172);

    public static final long FADE_RESCHEDULE_DELAY = 80;

    public static final int FADE_IN_INCREMENT = 15;

    public static final int FADE_OUT_INCREMENT = -20;

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "setAlpha".
     * 
     * @param shell
     * @param value
     * @return
     */
    public static boolean setAlpha(Shell shell, int value) {
        Method method = null;
        try {
            method = shell.getClass().getMethod("setAlpha", new Class[] { int.class }); //$NON-NLS-1$
            method.setAccessible(true);
            // shell.setAlpha(value);
            method.invoke(shell, new Object[] { value });
            return true;
        } catch (Exception e) {
            // ignore, not supported on Eclipse 3.3
            return false;
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getAlpha".
     * 
     * @param shell
     * @return
     */
    public static int getAlpha(Shell shell) {
        Method method = null;
        try {
            method = shell.getClass().getMethod("getAlpha"); //$NON-NLS-1$
            method.setAccessible(true);
            return (Integer) method.invoke(shell);
        } catch (Exception e) {
            return 0xFF;
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "fadeIn".
     * 
     * @param shell
     * @param listener
     * @return
     */
    public static FadeJob fadeIn(Shell shell, IFadeListener listener) {
        return new FadeJob(shell, FADE_IN_INCREMENT, FADE_RESCHEDULE_DELAY, listener);
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "fadeOut".
     * 
     * @param shell
     * @param listener
     * @return
     */
    public static FadeJob fadeOut(Shell shell, IFadeListener listener) {
        return new FadeJob(shell, FADE_OUT_INCREMENT, FADE_RESCHEDULE_DELAY, listener);
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "fade".
     * 
     * @param shell
     * @param fadeIn
     * @param increment
     * @param speed
     */
    public static void fade(Shell shell, boolean fadeIn, int increment, int speed) {
        try {
            Method method = shell.getClass().getMethod("setAlpha", new Class[] { int.class }); //$NON-NLS-1$
            method.setAccessible(true);

            if (fadeIn) {
                for (int i = 0; i <= 255; i += increment) {
                    // shell.setAlpha(i);
                    method.invoke(shell, new Object[] { i });
                    try {
                        Thread.sleep(speed);
                    } catch (InterruptedException e) {
                        // ignore
                    }
                }
                // shell.setAlpha(255);
                method.invoke(shell, new Object[] { 255 });
            } else {
                for (int i = 244; i >= 0; i -= increment) {
                    // shell.setAlpha(i);
                    method.invoke(shell, new Object[] { i });
                    try {
                        Thread.sleep(speed);
                    } catch (InterruptedException e) {
                        // ignore
                    }
                }
                // shell.setAlpha(0);
                method.invoke(shell, new Object[] { 0 });
            }
        } catch (Exception e) {
            // ignore, not supported on Eclipse 3.3
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com SwtUtils class global comment. Detailled comment <br/>
     * 
     * $Id: Dengues.epf 2008-1-6 qiang.zhang $
     * 
     */
    public static class FadeJob extends Job {

        private final Shell shell;

        private final int increment;

        private volatile boolean stopped;

        private volatile int currentAlpha;

        private final long delay;

        private final IFadeListener fadeListener;

        /**
         * Qiang.Zhang.Adolf@gmail.com FadeJob constructor comment.
         * 
         * @param shell
         * @param increment
         * @param delay
         * @param fadeListener
         */
        public FadeJob(Shell shell, int increment, long delay, IFadeListener fadeListener) {
            super("Fading"); //$NON-NLS-1$
            if (increment < -255 || increment == 0 || increment > 255) {
                throw new IllegalArgumentException("-255 <= increment <= 255 && increment != 0"); //$NON-NLS-1$
            }
            if (delay < 1) {
                throw new IllegalArgumentException("delay must be > 0"); //$NON-NLS-1$
            }
            this.currentAlpha = getAlpha(shell);
            this.shell = shell;
            this.increment = increment;
            this.delay = delay;
            this.fadeListener = fadeListener;

            setSystem(true);
            schedule(delay);
        }

        @Override
        protected void canceling() {
            stopped = true;
        }

        private void reschedule() {
            if (stopped) {
                return;
            }
            schedule(delay);
        }

        public void cancelAndWait(final boolean setAlpha) {
            if (stopped) {
                return;
            }
            cancel();
            Display.getDefault().syncExec(new Runnable() {

                public void run() {
                    if (setAlpha) {
                        SwtUtils.setAlpha(shell, getLastAlpha());
                    }
                }
            });
        }

        @Override
        protected IStatus run(IProgressMonitor monitor) {
            if (stopped) {
                return Status.OK_STATUS;
            }

            currentAlpha += increment;
            if (currentAlpha <= 0) {
                currentAlpha = 0;
            } else if (currentAlpha >= 255) {
                currentAlpha = 255;
            }

            Display.getDefault().syncExec(new Runnable() {

                public void run() {
                    if (stopped) {
                        return;
                    }

                    if (shell.isDisposed()) {
                        stopped = true;
                        return;
                    }

                    if (!SwtUtils.setAlpha(shell, currentAlpha)) {
                        // just in case it failed for some other reason than lack of support on the platform
                        currentAlpha = getLastAlpha();
                        SwtUtils.setAlpha(shell, currentAlpha);
                        stopped = true;
                    }

                    if (fadeListener != null) {
                        fadeListener.faded(shell, currentAlpha);
                    }
                }
            });

            if (currentAlpha == 0 || currentAlpha == 255) {
                stopped = true;
            }

            reschedule();
            return Status.OK_STATUS;
        }

        private int getLastAlpha() {
            return (increment < 0) ? 0 : 255;
        }

    }

    /**
     * Qiang.Zhang.Adolf@gmail.com SwtUtils class global comment. Detailled comment <br/>
     * 
     * $Id: Dengues.epf 2008-1-6 qiang.zhang $
     * 
     */
    public static interface IFadeListener {

        public void faded(Shell shell, int alpha);

    }

}
